package com.baitiaojun.bulletscreen.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baitiaojun.api.domain.video.BulletScreen;
import com.baitiaojun.api.exception.ConditionException;
import com.baitiaojun.bulletscreen.mapper.BulletScreenMapper;
import com.baitiaojun.bulletscreen.service.BulletScreenService;
import com.baitiaojun.bulletscreen.support.RedisSupport;
import io.netty.util.internal.StringUtil;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * @类名 BulletScreenServiceImpl
 * @描述 TODO
 * @作者 白条君
 * @创建日期 2022/12/31 17:24
 * @版本 1.0
 */
@Service("bulletScreenServiceImpl")
public class BulletScreenServiceImpl implements BulletScreenService {

    @Resource
    private BulletScreenMapper bulletScreenMapper;

    @Autowired
    private RedisSupport redisSupport;

    private static final Object LOCK = new Object();

    private static final Lock REENTRANT_LOCK = new ReentrantLock();

    @Override
    public void saveBulletScreen(BulletScreen bulletScreen) throws ConditionException {
        int res = bulletScreenMapper.insertSelective(bulletScreen);
        if (res == 0) {
            throw new ConditionException("添加弹幕到数据库失败");
        }
    }

    @Override
    public List<BulletScreen> getBulletScreen(Long videoId, String startTime, String endTime) {
        Map<String, Object> params = new HashMap<>();
        Object value = redisSupport.getValue(String.valueOf(videoId));
        if (ObjectUtils.isNotEmpty(value)) {
            List<BulletScreen> bulletScreens = JSONObject.parseArray(JSONObject.toJSONString(value), BulletScreen.class);
            if (StringUtil.isNullOrEmpty(startTime) && StringUtil.isNullOrEmpty(endTime)) {
                return bulletScreens;
            } else if (!StringUtil.isNullOrEmpty(startTime) && !StringUtil.isNullOrEmpty(endTime)) {
                params.put("startTime", startTime);
                params.put("endTime", endTime);
                return this.filter(bulletScreens, params);
            } else if (!StringUtil.isNullOrEmpty(startTime)) {
                params.put("startTime", startTime);
                return this.filter(bulletScreens, params);
            } else if (!StringUtil.isNullOrEmpty(endTime)) {
                params.put("endTime", endTime);
                return this.filter(bulletScreens, params);
            }
        }
        List<BulletScreen> bulletScreens = new ArrayList<>();
        synchronized (LOCK) {
            value = redisSupport.getValue(String.valueOf(videoId));
            if (ObjectUtils.isEmpty(value)) {
                bulletScreens = bulletScreenMapper.selectBulletScreens(videoId);
                batchSyncRedis(videoId, bulletScreens);
            }
        }
        if (!StringUtil.isNullOrEmpty(startTime) && !StringUtil.isNullOrEmpty(endTime)) {
            params.put("startTime", startTime);
            params.put("endTime", endTime);
            return this.filter(bulletScreens, params);
        }
        return bulletScreens;
    }

    @Override
    public List<BulletScreen> filter(List<BulletScreen> bulletScreens, Map<String, Object> params) {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        List<BulletScreen> bulletScreenList = new ArrayList<>();
        if (params.size() == 2) {
            bulletScreenList = bulletScreens.stream().filter(bulletScreen -> {
                String createTime = format.format(bulletScreen.getCreatetime());
                return createTime.compareTo(params.get("startTime").toString()) >= 0
                        && createTime.compareTo(params.get("endTime").toString()) <= 0;
            }).collect(Collectors.toList());
        } else if (params.size() == 1) {
            Set<Map.Entry<String, Object>> entries = params.entrySet();
            for (Map.Entry<String, Object> entry : entries) {
                if ("startTime".equals(entry.getKey())) {
                    bulletScreenList = bulletScreens.stream().filter(bulletScreen -> {
                        String createTime = format.format(bulletScreen.getCreatetime());
                        return createTime.compareTo(entry.getValue().toString()) >= 0;
                    }).collect(Collectors.toList());
                } else if ("endTime".equals(entry.getKey())) {
                    bulletScreenList = bulletScreens.stream().filter(bulletScreen -> {
                        String createTime = format.format(bulletScreen.getCreatetime());
                        return createTime.compareTo(entry.getValue().toString()) <= 0;
                    }).collect(Collectors.toList());
                }
            }
        }
        return bulletScreenList;
    }

    @Async
    @Override
    public void batchSyncRedis(Long videoId, List<BulletScreen> bulletScreens) {
        List<Map<String, Object>> bulletScreenList = new ArrayList<>();
        Map<String, Object> bulletScreenMap;
        for (BulletScreen bulletScreen : bulletScreens) {
            bulletScreenMap = new HashMap<>();
            bulletScreenMap.put("content", bulletScreen.getContent());
            bulletScreenMap.put("createtime", bulletScreen.getCreatetime());
            bulletScreenList.add(bulletScreenMap);
        }
        redisSupport.setValue(String.valueOf(videoId), bulletScreenList, 1, TimeUnit.DAYS);
    }

    @Async
    @Override
    public void syncRedis(Long videoId, Map<String, Object> content) {
        //redis同步信号量
        boolean flag = true;
        String key = String.valueOf(videoId);
        JSONArray bulletScreens = new JSONArray();
        //每个线程自旋获取同一把锁进行redis批操作
        while (flag) {
            if (REENTRANT_LOCK.tryLock()) {
                try {
                    //先获取这个视频原来的弹幕列表
                    Object value = redisSupport.getValue(key);
                    if (ObjectUtils.isNotEmpty(value)) {
                        bulletScreens = JSONObject.parseArray(JSONObject.toJSONString(value));
                    }
                    //将新的弹幕添加到这个列表
                    bulletScreens.add(content);
                    //将所有弹幕添加到这个key保存
                    redisSupport.setValue(key, bulletScreens, 1, TimeUnit.DAYS);
                } finally {
                    REENTRANT_LOCK.unlock();
                    flag = false;
                }
            }
        }
    }

    @Override
    public void batchSaveBulletScreenMqDuplicate(List<BulletScreen> bulletScreens) throws ConditionException {
        int res = bulletScreenMapper.batchInsertBulletScreenMqDuplicate(bulletScreens);
        if (bulletScreens.size() != res) {
            throw new ConditionException("批量添加冗余存储失败");
        }
    }
}
